package com.ihr360.payroll.service.salaryreport.impl;

import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.model.vo.SimpleAuthUserResourceVo;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.commons.vo.PageData;
import com.ihr360.excel.ExcelUtil;
import com.ihr360.excel.entity.ExportParams;
import com.ihr360.payroll.dao.MergeReportWithSalaryPlanRepository;
import com.ihr360.payroll.dao.MergeSalaryReportRepository;
import com.ihr360.payroll.dao.MergeSalaryReportTermSalaryRepository;
import com.ihr360.payroll.entity.salary.SalaryReportPlanEntity;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.report.MergeReportWithSalaryPlan;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReport;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReportTermSalary;
import com.ihr360.payroll.model.dto.salary.report.embedded.MergeSalaryField;
import com.ihr360.payroll.model.dto.salary.report.embedded.SalaryFieldSnapshot;
import com.ihr360.payroll.model.response.salaryreport.MergeSalaryReportResponse;
import com.ihr360.payroll.model.response.salaryreport.SalaryHeader;
import com.ihr360.payroll.model.response.salaryreport.SalaryReportResponse;
import com.ihr360.payroll.model.response.salaryreport.StaffSalaryResponse;
import com.ihr360.payroll.model.vo.SalaryFieldEntity;
import com.ihr360.payroll.request.salaryReport.MergeSalaryQueryRequest;
import com.ihr360.payroll.request.salaryReport.MergeSalaryReportRequest;
import com.ihr360.payroll.request.salaryReport.MergeSalaryReportSalaryField;
import com.ihr360.payroll.request.salaryReport.SalaryReportRequest;
import com.ihr360.payroll.response.salaryreport.MergeReportMonthResponse;
import com.ihr360.payroll.service.salaryplan.SalaryPlanService;
import com.ihr360.payroll.service.salaryreport.MergeReportStaffSalaryService;
import com.ihr360.payroll.service.salaryreport.MergeSalaryReportService;
import com.ihr360.payroll.service.salaryreport.MergeSalaryReportTermSalaryService;
import com.ihr360.payroll.service.salaryreport.job.MergeSalaryReportDataJob;
import com.ihr360.payroll.type.ReportState;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Eric on 2018/4/12/012.
 */
@Service
public class MergeSalaryReportServiceImpl extends AbstractSimpleRestServiceImpl<MergeSalaryReport, Long> implements MergeSalaryReportService {

    private Logger logger = LoggerFactory.getLogger(MergeSalaryReportServiceImpl.class);


    @Autowired
    private MergeSalaryReportRepository mergeSalaryReportRepository;

    @Autowired
    private MergeSalaryReportDataJob mergeSalaryReportDataJob;

    @Autowired
    private MergeSalaryReportTermSalaryService mergeSalaryReportTermSalaryService;

    @Autowired
    private MergeReportStaffSalaryService mergeReportStaffSalaryService;

    @Autowired
    private SalaryPlanService salaryPlanService;

    @Autowired
    private MergeReportWithSalaryPlanRepository mergeReportWithSalaryPlanRepository;

    @Autowired
    private MergeSalaryReportTermSalaryRepository mergeSalaryReportTermSalaryRepository;

    @Override
    public SimpleRestReposity<MergeSalaryReport, Long> getSimpleRestReposity() {
        return mergeSalaryReportRepository;
    }

    @Override
    public List<SalaryReportPlanEntity> getPlanList(String companyId) {
        List<SalaryReportPlanEntity> result = Lists.newArrayList();
        List<MergeSalaryReport> list = (List<MergeSalaryReport>) this.getAllInCompany();
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        for (MergeSalaryReport mergeSalaryReport : list) {
            result.add(mergeReportToEntity(mergeSalaryReport));
        }
        return result;
    }

    private SalaryReportPlanEntity mergeReportToEntity(MergeSalaryReport mergeSalaryReport) {
        if (mergeSalaryReport == null) {
            return null;
        }
        SalaryReportPlanEntity entity = new SalaryReportPlanEntity();
        entity.setSalaryPlanId(mergeSalaryReport.getId());
        entity.setName(mergeSalaryReport.getName());
        entity.setEnableReport(true);
        return entity;
    }

    @Override
    public PageData<MergeSalaryReportResponse> queryMergeReportList(AuthUserInfo userInfo, SalaryReportRequest reportRequest) {
        List<MergeSalaryReportResponse> list = Lists.newArrayList();
        List<SimpleAuthUserResourceVo> authList = authUserResourceService.queryUserResources(userInfo.getCompanyId(), MergeSalaryReport.class, userInfo.getUserId());
        if (CollectionUtils.isEmpty(authList)) {
            return new PageData<>(0, 0, list);
        }
        List<Long> reportIdList = Lists.newArrayList();
        authList.forEach(authResource -> {
            reportIdList.add(Long.parseLong(authResource.getResourceId()));
        });
        Page<MergeSalaryReport> page = mergeSalaryReportRepository.findByCompanyIdAndIdIn(userInfo.getCompanyId(), reportIdList, reportRequest.getPageable());
        List<MergeSalaryReport> reportList = page.getContent();
        if (CollectionUtils.isEmpty(reportList)) {
            return new PageData<>(0, 0, list);
        }
        for (MergeSalaryReport mergeSalaryReport : reportList) {
            MergeSalaryReportResponse reportResponse = new MergeSalaryReportResponse();
            reportResponse.setId(mergeSalaryReport.getId());
            reportResponse.setName(mergeSalaryReport.getName());
            reportResponse.setRemark(mergeSalaryReport.getRemark());
            reportResponse.setMergeTax(mergeSalaryReport.getIsMergeTax());
            list.add(reportResponse);
        }
        return new PageData<>(page.getTotalPages(), page.getTotalElements(), list);
    }

    @Override
    public void updateMergeReportName(String companyId, Long mergeReportId, String name, String remark) {
        MergeSalaryReport mergeSalaryReport = mergeSalaryReportRepository.findByCompanyIdAndId(companyId, mergeReportId);
        mergeSalaryReport.setName(name);
        mergeSalaryReport.setRemark(remark);
        mergeSalaryReportRepository.save(mergeSalaryReport);
    }

    @Override
    @Transactional
    public void saveMergeReport(String companyId, MergeSalaryReportRequest request) {
        MergeSalaryReport report = this.getMergeSalaryReport(companyId, request);
        List<Long> planIdList = request.getSalaryPlanIdList();
        if (CollectionUtils.isEmpty(planIdList)) {
            return;
        }
        List<MergeReportWithSalaryPlan> planList = report.getPlanIdList();
        if (CollectionUtils.isEmpty(planList)) {
            planList = Lists.newArrayList();
        } else {
            planList.clear();
        }
        for (Long planId : planIdList) {
            MergeReportWithSalaryPlan plan = new MergeReportWithSalaryPlan();
            plan.setMergeReport(report);
            plan.setSalaryPlanId(planId);
            plan.setCompanyId(companyId);
            planList.add(plan);
        }
        this.createOrUpdate(report.getId(), report);
        //关联关系保存
        mergeReportWithSalaryPlanRepository.save(planList);
    }

    private MergeSalaryReport getMergeSalaryReport(String companyId, MergeSalaryReportRequest request) {
        MergeSalaryReport report = null;
        if (request.getId() != null) {
            report = this.getOne(request.getId());
            if (report == null) {
                throw new Ihr360Exception(null, "合并薪资报表不存在，无法编辑");
            }
            if (report.getIsMergeTax() != null && report.getIsMergeTax()) {
                throw new Ihr360Exception(null, "系统合并计税报表不可编辑");
            }
        } else {
            report = new MergeSalaryReport();
        }
        List<Long> salaryPlanIdList = request.getSalaryPlanIdList();
        if (CollectionUtils.isEmpty(salaryPlanIdList)) {
            throw new Ihr360Exception(null, "合并的薪资报表不可为空");
        }
        report.setName(request.getName());
        report.setRemark(request.getRemark());
        report.setId(request.getId());
        report.setMonthCycle(request.isMonthCycle());
        report.setCompanyId(companyId);
        //合并薪资方案
        List<SalaryPlan> salaryPlanList = salaryPlanService.getCompanySalaryPlan(salaryPlanIdList);
        if (CollectionUtils.isEmpty(salaryPlanList)) {
            throw new Ihr360Exception(null, "合并的薪资方案不存在");
        }
        List<String> idList = Lists.newArrayList();
        salaryPlanIdList.forEach(salaryPlanId -> {
            idList.add(salaryPlanId.toString());
        });
        report.setSalaryPlanList(idList);
        //查询薪资方案薪资表中所有字段
        Map<String, SalaryFieldSnapshot> salaryFieldMap = new HashMap<>();
        salaryPlanList.forEach(salaryPlan -> {
            if (salaryPlan != null && salaryPlan.getPayrollConfig() != null
                    && CollectionUtils.isNotEmpty(salaryPlan.getPayrollConfig().getFields())) {
                for (SalaryField salaryField : salaryPlan.getPayrollConfig().getFields()) {
                    if (salaryField == null) {
                        continue;
                    }
                    SalaryFieldSnapshot salaryFieldSnapshot = new SalaryFieldSnapshot();
                    BeanUtils.copyProperties(salaryField, salaryFieldSnapshot);
                    BeanUtils.copyProperties(salaryField.getValueMeta(), salaryFieldSnapshot);
                    salaryFieldMap.put(salaryFieldSnapshot.getCode(), salaryFieldSnapshot);
                }
            }
        });
        List<SalaryFieldSnapshot> mergeField = Lists.newArrayList();
        if (CollectionUtils.isEmpty(request.getFieldList())) {
            throw new Ihr360Exception(null, "合并的薪资字段不可为空");
        }
        for (MergeSalaryReportSalaryField field : request.getFieldList()) {
            if (salaryFieldMap.containsKey(field.getCode())) {
                SalaryFieldSnapshot salaryFieldSnapshot = salaryFieldMap.get(field.getCode());
                salaryFieldSnapshot.setName(field.getName());
                mergeField.add(salaryFieldSnapshot);
            } else {
                SalaryFieldSnapshot salaryFieldSnapshot = new SalaryFieldSnapshot();
                salaryFieldSnapshot.setName(field.getName());
                salaryFieldSnapshot.setCode(field.getCode());
            }
        }
        if (CollectionUtils.isEmpty(mergeField)) {
            throw new Ihr360Exception(null, "合并的薪资字段不可为空");
        }
        MergeSalaryField mergeSalaryField = new MergeSalaryField();
        mergeSalaryField.setFields(mergeField);
        report.setMergeSalaryField(mergeSalaryField);

        report.setJoinType(request.getJoinType());
        if (MergeSalaryReport.JoinType.LEFT_JOIN.equals(request.getJoinType())) {
            Long salaryPlanId = request.getJoinSalaryPlanId();
            report.setJoinSalaryPlanId(salaryPlanId);
        }
        return report;
    }

    @Override
    public SalaryReportResponse queryMergeReportSalaryList(String companyId, MergeSalaryQueryRequest reportRequest) {
        SalaryReportResponse reportResponse = new SalaryReportResponse();
        MergeSalaryReportTermSalary termSalary = mergeSalaryReportTermSalaryService.getTermSalary(companyId, reportRequest);
        if (termSalary != null) {
            PageData<StaffSalaryResponse> responsePageData = mergeReportStaffSalaryService.queryStaffSalaryPage(companyId, reportRequest, termSalary.getId());
            reportResponse.setStaffSalary(responsePageData);
        }
        return reportResponse;
    }

    @Override
    public SalaryReportResponse queryMergeReportSalaryHead(String companyId, MergeSalaryQueryRequest reportRequest) {
        SalaryReportResponse reportResponse = new SalaryReportResponse();
        MergeSalaryReportTermSalary termSalary = mergeSalaryReportTermSalaryService.getTermSalary(companyId, reportRequest);
        if (termSalary == null || ReportState.FINISHED != termSalary.getReportState()) {
            throw new Ihr360Exception(null, "该月合并结果数据正在计算中！");
        } else {
            reportResponse.setSalaryHeader(termSalary.getSalaryHeader());
        }
        return reportResponse;
    }

    @Override
    public List<Integer> queryMergeReportYearList(String companyId, Long mergeReportId) {
        List<Integer> list = Lists.newArrayList();
        MergeSalaryReport mergeSalaryReport = this.getOne(mergeReportId);
        if (mergeSalaryReport == null) {
            throw new Ihr360Exception(null, "合并报表不存在");
        }
        List<MergeSalaryReportTermSalary> termList = mergeSalaryReportTermSalaryRepository.findByCompanyIdAndMergeReportIdAndReportStateOrderByYear(companyId, mergeReportId, ReportState.FINISHED);
        if (CollectionUtils.isEmpty(termList)) {
            return list;
        }
        termList.forEach(term -> {
            Integer year = term.getYear();
            if (!list.contains(year)) {
                list.add(year);
            }
        });
        return list;
    }

    @Override
    public List<MergeReportMonthResponse> queryMergeReportMonthList(String companyId, Long mergeReportId, int year) {
        List<MergeReportMonthResponse> list = Lists.newArrayList();
        MergeSalaryReport mergeSalaryReport = this.getOne(mergeReportId);
        if (mergeSalaryReport == null) {
            throw new Ihr360Exception(null, "合并报表不存在");
        }
        List<MergeSalaryReportTermSalary> termList = mergeSalaryReportTermSalaryRepository.findByCompanyIdAndMergeReportIdAndYearAndReportState(companyId, mergeReportId, year, ReportState.FINISHED);
        if (CollectionUtils.isEmpty(termList)) {
            return list;
        }
        termList.forEach(term -> {
            MergeReportMonthResponse reportMonthResponse = new MergeReportMonthResponse();
            reportMonthResponse.setMonth(term.getMonth());
            reportMonthResponse.setTermIndex(term.getIndexno());
            list.add(reportMonthResponse);
        });
        return list;
    }

    @Override
    public void exportMergeReportSalaryList(String companyId, MergeSalaryQueryRequest reportRequest, OutputStream out) {
        SalaryHeader salaryHeader;//表头信息
        List<StaffSalaryResponse> salaryResponseList;//员工薪资信息
        //查询临时生成记录,如存在直接查询临时记录
        MergeSalaryReportTermSalary termSalary = mergeSalaryReportTermSalaryService.getTermSalary(companyId, reportRequest);
        if (termSalary == null) {
            throw new Ihr360Exception(null, "暂无数据");
        }
        salaryHeader = termSalary.getSalaryHeader();
        if (salaryHeader == null) {
            throw new Ihr360Exception(null, "暂无数据导出");
        }
        salaryResponseList = mergeReportStaffSalaryService.queryStaffSalaryList(companyId, termSalary.getId());

        //开始导出处理
        Map<String, String> headerMap = getExportMergeReportHeaderMap(salaryHeader);
        ExportParams exportParams = new ExportParams();
        exportParams.setHeaderMap(headerMap);
        //获取数据
        List<List<Object>> datas = this.getMergeSalaryDateList(salaryResponseList, salaryHeader);
        //导出
        try {
            exportParams.setRowDatas(datas);
            ExcelUtil.exportExcel(exportParams, out);
            out.close();
        } catch (IOException e) {
            logger.error("合并报表导出失败：" + e);
            throw new Ihr360Exception(null, "合并报表数据导出失败！");
        }
    }

    /**
     * 组装合并报表导出数据
     */
    private List<List<Object>> getMergeSalaryDateList(List<StaffSalaryResponse> list, SalaryHeader salaryHeader) {
        List<List<Object>> datas = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return datas;
        }
        if (salaryHeader == null || CollectionUtils.isEmpty(salaryHeader.getHeadMap())) {
            return datas;
        }
        Map<String, SalaryFieldEntity> headMap = salaryHeader.getHeadMap();
        for (StaffSalaryResponse response : list) {
            if (response == null) {
                continue;
            }
            List<Object> rowData = new ArrayList<>();
            rowData.add(response.getStaffName());
            rowData.add(response.getDepartmentName());
            rowData.add(response.getMobileNo());
            if (response.getSummaryData() != null && MapUtils.isNotEmpty(response.getSummaryData().getData())) {
                Map<String, DynamicCell> summaryData = response.getSummaryData().getData();
                for (Map.Entry<String, SalaryFieldEntity> entry : headMap.entrySet()) {
                    SalaryFieldEntity entity = entry.getValue();
                    String code = entity.getColumnName();
                    if (SyncedBasicStaffInfo.Field.staffName.equals(code) || SyncedBasicStaffInfo.Field.departmentName.equals(code) ||
                            SyncedBasicStaffInfo.Field.mobileNo.equals(code)) {
                        continue;
                    }
                    if (summaryData.containsKey(code)) {
                        DynamicCell cell = summaryData.get(code);
                        if (cell != null) {
                            rowData.add(cell.getCellValue());
                        }
                    } else {
                        rowData.add("");
                    }
                }
            }
            datas.add(rowData);
        }
        return datas;
    }


    /**
     * 获取合并报表导出表头
     */
    private Map<String, String> getExportMergeReportHeaderMap(SalaryHeader header) {
        Map<String, String> headerMap = new LinkedHashMap<>();
        for (Map.Entry<String, SalaryFieldEntity> entry : header.getHeadMap().entrySet()) {
            SalaryFieldEntity entity = entry.getValue();
            headerMap.put(entity.getColumnName(), entity.getFieldName());
        }
        return headerMap;
    }
}
